package com.zgzg.shop.pay.base.utils;

import cn.hutool.core.lang.TypeReference;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.SetOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.core.script.RedisScript;

import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
public class RedisUtil {
    private static StringRedisTemplate stringRedisTemplate;
    private static final ObjectMapper jsonMapper = new ObjectMapper();

    static {
        RedisUtil.stringRedisTemplate = SpringUtil.getBean(StringRedisTemplate.class);
    }

    public static boolean incr(String key, long timeout, TimeUnit unit) {
        boolean result = false;
        try {
            ValueOperations<String, String> operations = stringRedisTemplate.opsForValue();
            operations.increment(key);
            operations.getOperations().expire(key, timeout, unit);
            result = true;
        } catch (Exception e) {
            log.error("数据自增异常", e);
        }
        return result;
    }

    public static boolean add2Set(String key, long timeout, TimeUnit unit, String... values) {
        boolean result = false;
        try {
            SetOperations<String, String> operations = stringRedisTemplate.opsForSet();
            operations.add(key, values);
            operations.getOperations().expire(key, timeout, unit);
            result = true;
        } catch (Exception e) {
            log.error("数据自增异常", e);
        }
        return result;
    }

    public static Long setSize(String key) {
        try {
            SetOperations<String, String> operations = stringRedisTemplate.opsForSet();
            return operations.size(key);
        } catch (Exception e) {
            log.error("数据自增异常", e);
        }
        return null;
    }

    public static<T> T get(final String key, Class<T> tClass) {
        String result;
        try {
            ValueOperations<String, String> operations = stringRedisTemplate.opsForValue();
            result = operations.get(key);
            return toBeanOrNull(result, tClass);
        } catch (Exception e) {
            log.error("从redis读取缓存时出现异常 ", e);
        }
        return null;
    }

    public static<T> T get(final String key, TypeReference<T> typeReference) {
        String result;
        try {
            ValueOperations<String, String> operations = stringRedisTemplate.opsForValue();
            result = operations.get(key);
            return JSONUtil.toBean(result, typeReference, true);
        } catch (Exception e) {
            log.error("从redis读取缓存时出现异常 ", e);
        }
        return null;
    }

    public static void set(String key, Object o, long expire, TimeUnit timeUnit) {
        try {
            String value = JSONUtil.toJsonStr(o);
            ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();
            ops.set(key, value, expire, timeUnit);
        } catch (Exception e) {
            log.error("写入redis时出现异常", e);
        }
    }

    public static void hashPut(String key, String hK, Object hV, long expire, TimeUnit timeUnit) {
        try {
            String value = JSONUtil.toJsonStr(hV);
            HashOperations<String, String, String> hashOperations = stringRedisTemplate.opsForHash();
            hashOperations.put(key, hK, value);
            hashOperations.getOperations().expire(key, expire, timeUnit);
        } catch (Exception e) {
            log.error("写入redis时出现异常", e);
        }
    }

    public static<T> T hashGet(String key, String hK, Class<T> tClass) {
        try {
            HashOperations<String, String, String> hashOperations = stringRedisTemplate.opsForHash();
            String hValue = hashOperations.get(key, hK);
            return toBeanOrNull(hValue, tClass);
        } catch (Exception e) {
            log.error("写入redis时出现异常", e);
        }
        return null;
    }

    public static<T> T hashGet(String key, String hK, TypeReference<T> typeReference) {
        try {
            HashOperations<String, String, String> hashOperations = stringRedisTemplate.opsForHash();
            String hValue = hashOperations.get(key, hK);
            return toBeanOrNull(hValue, typeReference);
        } catch (Exception e) {
            log.error("写入redis时出现异常", e);
        }
        return null;
    }

    public static<T> Map<String, T> hashGetAll(String key, Class<T> tClass) {
        try {
            HashOperations<String, String, String> hashOperations = stringRedisTemplate.opsForHash();
            Map<String, String> entries = hashOperations.entries(key);
            return entries.entrySet().stream().collect(Collectors.toMap(
                    Map.Entry::getKey, entry -> toBeanOrNull(entry.getValue(), tClass), (v1, v2) -> v1
            ));
        } catch (Exception e) {
            log.error("写入redis时出现异常", e);
        }
        return null;
    }

    public static<T> T execute(Class<T> tClass, String script, List<String> keys, Object... args) {
        RedisScript<T> redisScript = RedisScript.of(script, tClass);
        return stringRedisTemplate.execute(redisScript, keys, args);
    }

    public static Boolean hasKey(String key) {
        return stringRedisTemplate.hasKey(key);
    }

    public static Boolean delete(String key) {
        return stringRedisTemplate.delete(key);
    }

    private static <T> T toBeanOrNull(String json, Class<T> tClass) {
        return json == null ? null : toObj(json, tClass);
    }

    private static <T> T toBeanOrNull(String json, TypeReference<T> typeReference) {
        return json == null ? null : JSONUtil.toBean(json, typeReference, true);
    }

    public static <T> T toObj(String str, Class<T> clz) {
        try {
            if (clz == String.class) {
                return clz.cast(str);
            }
            return jsonMapper.readValue(str, clz);
        } catch (JsonProcessingException e) {
            throw new UnsupportedOperationException(e);
        }
    }
}
